#include "ZTextInput.h"
#include "ZControlFactory.h"
#include "ZPaintManager.h"

namespace ZuiLib
{

ZTextInput::ZTextInput(void)
{
	m_uButtonState=0;
	m_bMouseEnabled=true;
	m_caret=new RefCountedObject<ZCaret>(this);
	m_selBegin=0;
	m_selEnd=0;
	m_scroll=0;
	m_caretPos=0;

	m_editingBegin=0;
	m_editingEnd=0;
	m_editingOffset=0;

	m_needResetText=true;
	m_textDraw->SetAlign(DT_VCENTER | DT_SINGLELINE);
}


ZTextInput::~ZTextInput(void)
{
	m_caret=NULL;
}


const char* ZTextInput::GetClass() const
{
	return "TextInput";
}


void* ZTextInput::GetInterface(const char* pstrName)
{
	if( strcmp(pstrName, "TextInput") == 0 ) 
		return static_cast<ZTextInput*>(this);
	else
		return ZLabel::GetInterface(pstrName);
}

void ZTextInput::SetPos(RECT rcItem)
{
	ZLabel::SetPos(rcItem);

	if(IsFocused())
	{
		RECT rcCaret=GetCaretRect();
		m_caret->SetPos(rcCaret);
		m_caret->ShowCaret(true);

		ZRect rc(rcCaret);
		SDL_SetTextInputRect(&rc);
	}
}


ZString ZTextInput::GetText() const
{
	ZString textUtf8;
	Utf16ToUtf8(&m_textUtf16,&textUtf8);
	return textUtf8;
}


void ZTextInput::SetText(const char* buf)
{
	ZString strUtf8(buf);
	Utf8ToUtf16(&strUtf8,&m_textUtf16);
	Invalidate();
}


void ZTextInput::Copy()
{
	ZString textUtf8;
	if(m_selEnd != m_selBegin)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		ZWString text=m_textUtf16.Mid(selBegin,selEnd-selBegin);
		Utf16ToUtf8(&text,&textUtf8);
	}
	SDL_SetClipboardText(textUtf8.GetString());
}


void ZTextInput::Cut()
{
	ZString textUtf8;
	if(m_selEnd != m_selBegin)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		ZWString text=m_textUtf16.Mid(selBegin,selEnd-selBegin);
		Utf16ToUtf8(&text,&textUtf8);
	}
	SDL_SetClipboardText(textUtf8.GetString());

	if(m_selBegin!=m_selEnd)
	{
		RemoveSel();
		int scrollMax=GetScrollMax();
		if (m_scroll>scrollMax)
		{
			m_scroll=scrollMax;
		}
		UpdateText();
	}
}

void ZTextInput::Paste()
{
	ZWString utf16;
	ZString textUtf8=SDL_GetClipboardText();
	Utf8ToUtf16(&textUtf8,&utf16);
	RemoveSel();

	ZWString temp;
	const ZWChar* str=utf16.GetString();

	while (*str)
	{
		if (*str!='\r' && *str!='\n')
		{
			temp.Append(*str);
		}
		++str;
	}

	m_textUtf16.Insert(m_caretPos,temp);
	m_caretPos+=temp.GetLength();
	UpdateText();
}

void ZTextInput::SelAll()
{
	m_selBegin=0;
	m_selEnd=m_textUtf16.GetLength();
	Invalidate();
}

void ZTextInput::SelNone()
{
	m_selBegin=m_caretPos;
	m_selEnd=m_caretPos;
	Invalidate();
}

void ZTextInput::DoEvent(TEventUI& event)
{
	switch(event.Type)
	{
	case UIEVENT_SETFOCUS:
		{
			RECT rcCaret=GetCaretRect();
			m_caret->SetPos(rcCaret);
			m_caret->ShowCaret(true);

			ZRect rc(rcCaret);
			SDL_StartTextInput();
			SDL_SetTextInputRect(&rc);
		}
		break;
	case UIEVENT_KILLFOCUS:
		m_caret->ShowCaret(false);
		SDL_StopTextInput();
		break;

	case UIEVENT_BUTTONDOWN:
		{
			SetCaretPos(event.ptMouse);

			m_selBegin=m_caretPos;
			m_selEnd=m_caretPos;

			m_editingBegin=m_caretPos;
			m_editingEnd=m_caretPos;
			m_editingOffset=0;

			m_uButtonState|=UISTATE_PUSHED;
			Invalidate();
		}
		break;

	case UIEVENT_BUTTONUP:
		{
			m_uButtonState &= ~UISTATE_PUSHED;
		}
		break;

	case UIEVENT_MOUSEMOVE:
		{
			if ((m_uButtonState & UISTATE_PUSHED)!=0)
			{
				m_selEnd=CharFromPos(event.ptMouse);
				Invalidate();
			}
		}
		break;

	case UIEVENT_KEYDOWN:
		{
			SDL_Keysym* key=(SDL_Keysym*)event.wParam;
			switch(key->sym)
			{
			case SDLK_BACKSPACE:
				if (m_editingBegin==m_editingEnd)
				{
					if (m_textUtf16.IsEmpty())
						return;
					if (m_selBegin==m_selEnd)
					{
						if(m_caretPos>0)
							m_textUtf16.Remove(--m_caretPos);
					}
					else
					{
						RemoveSel();
					}

					int scrollMax=GetScrollMax();
					if (m_scroll>scrollMax)
					{
						m_scroll=scrollMax;
					}
					UpdateText();
					SignalTextChanged(this);
				}
				break;

			case SDLK_DELETE:
				if (m_editingBegin==m_editingEnd)
				{
					if (m_textUtf16.IsEmpty())
						return;
					if (m_selBegin==m_selEnd)
					{
						if(m_caretPos>0)
							m_textUtf16.Remove(m_caretPos);
					}
					else
					{
						RemoveSel();
					}

					int scrollMax=GetScrollMax();
					if (m_scroll>scrollMax)
					{
						m_scroll=scrollMax;
					}
					UpdateText();
					SignalTextChanged(this);
				}
				break;

			case SDLK_LEFT:
				m_selBegin=m_caretPos;
				m_selEnd=m_caretPos;
				if(m_caretPos>0)
				{
					--m_caretPos;
					UpdateScroll();
				}
				break;
			case SDLK_RIGHT:
				if (m_caretPos<m_textUtf16.GetLength())
				{
					m_selBegin=m_selEnd;
					++m_caretPos;
					UpdateScroll();
				}
				break;

			case SDLK_c:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Copy();
					}
				}
				break;

			case SDLK_x:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Cut();
						SignalTextChanged(this);
					}
				}
				break;

			case SDLK_v:
				{
					if (CheckKeyState(KS_CTRL))
					{
						Paste();
						SignalTextChanged(this);
					}
				}
				break;
			case SDLK_a:
				{
					if (CheckKeyState(KS_CTRL))
					{
						SelAll();
					}
				}
				break;
			}
		}
		break;
	case UIEVENT_TEXTINPUT:
		{
			SDL_TextInputEvent* sdlEv=(SDL_TextInputEvent*)event.wParam;
			ZString utf8=sdlEv->text;
			ZWString utf16;

			RemoveSel();
			RemoveEditing();

			Utf8ToUtf16(&utf8,&utf16);
			m_textUtf16.Insert(m_caretPos,utf16);
			m_caretPos+=utf16.GetLength();
			UpdateScroll();
			SignalTextChanged(this);
		}
		break;
	case UIEVENT_TEXTEDITING:
		{
			SDL_TextEditingEvent* sdlEv=(SDL_TextEditingEvent*)event.wParam;
			ZString utf8(sdlEv->text);
			ZWString utf16;
			Utf8ToUtf16(&utf8,&utf16);

			RemoveSel();
			RemoveEditing();

			int len=utf16.GetLength();
			m_editingBegin=m_caretPos;
			m_editingEnd=m_editingBegin+len;

			m_editingOffset=ZMIN(sdlEv->start,len);

			m_textUtf16.Insert(m_caretPos,utf16);
			m_caretPos+=m_editingOffset;

			int scrollMax=GetScrollMax();
			if (m_scroll>scrollMax)
			{
				m_scroll=scrollMax;
			}

			UpdateScroll();
		}
		break;
	}
}


void ZTextInput::RemoveSel()
{
	if(m_selBegin!=m_selEnd)
	{
		int selBegin=m_selBegin;
		int selEnd=m_selEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_selEnd;
			selEnd=m_selBegin;
		}

		m_textUtf16.Remove(selBegin,selEnd-selBegin);
		m_selEnd=selBegin;
		m_selBegin=selBegin;
		m_caretPos=selBegin;
	}
}

void ZTextInput::RemoveEditing()
{
	if(m_editingBegin!=m_editingEnd)
	{
		int selBegin=m_editingBegin;
		int selEnd=m_editingEnd;
		if(selBegin>selEnd)
		{
			selBegin=m_editingEnd;
			selEnd=m_editingBegin;
		}

		m_textUtf16.Remove(selBegin,selEnd-selBegin);
		m_editingEnd=selBegin;
		m_editingBegin=selBegin;
		m_caretPos=selBegin;
	}
}

void ZTextInput::UpdateText()
{
	m_needResetText=true;
	Invalidate();
}

int ZTextInput::GetScrollMax()
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	//caretPos>scroll 计算滚动位置
	RefCountedPtr<ZFont> font=GetRenerder()->GetFontMgr()->GetFont(m_textDraw->GetFont());
	if (font)
	{
		int width=0;
		for (int i=m_textUtf16.GetLength()-1;i>=0;--i)
		{
			ZSize size;
			font->CharSize(m_textUtf16[i],size);
			if (width+size.w> rc.right-rc.left)
			{
				return i+1;
				break;
			}
			else
				width+=size.w;
		}
	}
	return 0;
}


RECT ZTextInput::GetCaretRect()
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	ZWString textDraw=m_textUtf16.Mid(m_scroll);
	RefCountedPtr<ZFont> font=GetRenerder()->GetFontMgr()->GetFont(m_textDraw->GetFont());
	if (font)
	{
		int caretPos=rc.left;
		int width=0;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			ZSize size;
			font->CharSize(textDraw[i],size);
			if (width+size.w > rc.right-rc.left || m_caretPos==i+m_scroll)
			{
				break;
			}
			else
			{
				width+=size.w;
				caretPos+=size.w;
			}
		}

		RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top-font->Height()-4)/2,caretPos+1,0};
		rcCaret.bottom=rcCaret.top+font->Height()+2;
		return rcCaret;
	}
	else
	{
		RECT rcCaret={rc.left,rc.top,rc.left+1,rc.bottom};
		return rcCaret;
	}
}

void ZTextInput::SetCaretPos(POINT pt)
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	ZWString textDraw=m_textUtf16.Mid(m_scroll);
	RefCountedPtr<ZFont> font=GetRenerder()->GetFontMgr()->GetFont(m_textDraw->GetFont());
	if (font)
	{
		int caretPos=rc.left;
		int width=0;
		int offset=pt.x-rc.left;
		int caret=m_scroll;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			ZSize size;
			font->CharSize(textDraw[i],size);
			if (width+size.w > offset)
			{
				if( offset>(width+size.w/2))
				{
					++caret;
					caretPos+=size.w;
				}
				break;
			}
			else
			{
				++caret;
				width+=size.w;
				caretPos+=size.w;
			}
		}
		m_caretPos=caret;
		RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top-font->Height()-4)/2,caretPos+1,0};
		rcCaret.bottom=rcCaret.top+font->Height()+4;
		m_caret->SetPos(rcCaret);
		m_caret->ShowCaret(true);

		ZRect rc(rcCaret);
		SDL_StartTextInput();
		SDL_SetTextInputRect(&rc);
	}
}


void ZTextInput::UpdateScroll()
{
	if(m_caretPos<m_scroll)
		UpdateText();
	else
	{
		RECT rc = m_rcItem;
		rc.left += m_rcTextPadding.left;
		rc.right -= m_rcTextPadding.right;
		rc.top += m_rcTextPadding.top;
		rc.bottom -= m_rcTextPadding.bottom;

		//caretPos>scroll 计算滚动位置
		ZWString textDraw=m_textUtf16.Mid(m_scroll,m_caretPos-m_scroll);
		RefCountedPtr<ZFont> font=GetRenerder()->GetFontMgr()->GetFont(m_textDraw->GetFont());
		if (font)
		{
			int width=0;
			for (int i=textDraw.GetLength()-1;i>=0;--i)
			{
				ZSize size;
				font->CharSize(textDraw[i],size);
				if (width+size.w> rc.right-rc.left)
				{
					m_scroll=m_scroll+i+1;
					break;
				}
				else
					width+=size.w;
			}
		}
		UpdateText();
	}
}

void ZTextInput::PaintText(ZRenderer* render)
{
	if( m_dwTextColor == 0 ) m_dwTextColor = m_pManager->GetDefaultFontColor();
    if( m_dwDisabledTextColor == 0 ) m_dwDisabledTextColor = m_pManager->GetDefaultDisabledColor();

    RECT rc = m_rcItem;
    rc.left += m_rcTextPadding.left;
    rc.right -= m_rcTextPadding.right;
    rc.top += m_rcTextPadding.top;
    rc.bottom -= m_rcTextPadding.bottom;

	color_t color=m_dwTextColor;

    if( !IsEnabled() ) {
		color=m_dwDisabledTextColor;
    }

	if (m_needResetText)
	{
		if (m_caretPos<m_scroll)
		{
			m_scroll=m_caretPos;
		}

		ZWString textDraw=m_textUtf16.Mid(m_scroll);

		RefCountedPtr<ZFont> font=GetFont();
		if (font)
		{
			int caretPos=rc.left;
			int width=0;
			//从右侧开始计算scroll

			for (int i=0;i<textDraw.GetLength();++i)
			{
				ZSize size;
				font->CharSize(textDraw[i],size);
				if (width+size.w> rc.right-rc.left)
				{
					textDraw=textDraw.Left(i);
					break;
				}
				else
					width+=size.w;

				if(m_scroll+i <m_caretPos)
					caretPos+=size.w;
			}
			
			RECT rcCaret={caretPos,rc.top+(rc.bottom-rc.top-font->Height()-2)/2,caretPos+1,0};
			rcCaret.bottom=rcCaret.top+font->Height()+1;
			m_caret->SetPos(rcCaret);

			//更新输入法位置
			ZRect rc(rcCaret);
			SDL_SetTextInputRect(&rc);

			ZString utf8;
			Utf16ToUtf8(&textDraw,&utf8);
			m_textDraw->SetText(utf8);
		}
		m_needResetText=false;
	}

	if(m_selBegin!=m_selEnd)
		PaintTextBk(render);
	m_caret->Draw(render);
	m_textDraw->Draw(render,color,rc);

	if(m_editingBegin!=m_editingEnd)
		PaintTextEditing(render);
}



int ZTextInput::CharFromPos(POINT pt)
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	ZWString textDraw=m_textUtf16.Mid(m_scroll);
	RefCountedPtr<ZFont> font=GetFont();
	if (font)
	{
		int width=0;
		int offset=pt.x-rc.left;
		int caret=m_scroll;
		for (int i=0;i<textDraw.GetLength();++i)
		{
			ZSize size;
			font->CharSize(textDraw[i],size);
			if (width+size.w > offset)
			{
				if( offset>(width+size.w/2))
				{
					++caret;
				}
				break;
			}
			else
			{
				++caret;
				width+=size.w;
			}
		}
		return caret;
	}
	return 0;
}


POINT ZTextInput::PosFromChar(int nCh)
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	POINT pt={rc.left,rc.top};
	RefCountedPtr<ZFont> font=GetFont();

	if (nCh<=m_scroll)
	{
		return pt;
	}

	int width=0;
	for (int i=m_scroll;i<nCh;++i)
	{
		width+= font->CharWidth(m_textUtf16[i]);
	}
	pt.x+=width;
	return pt;
}




void ZTextInput::PaintTextBk(ZRenderer* render)
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	int selBegin=m_selBegin;
	int selEnd=m_selEnd;
	if(selBegin>selEnd)
	{
		selBegin=m_selEnd;
		selEnd=m_selBegin;
	}

	if (selEnd<m_scroll)
		return;
	if (selBegin<m_scroll)
		selBegin=m_scroll;


	RefCountedPtr<ZFont> font=GetFont();

	int leftOffset=0,rightOffset=0;
	for (int i=m_scroll;i<m_textUtf16.GetLength();++i)
	{
		int w=font->CharWidth(m_textUtf16[i]);
		if (selBegin>i)
		{
			leftOffset+=w;
		}
		if (selEnd>i)
		{
			rightOffset+=w;
		}
	}

	ZRect rcRect;
	rcRect.x=rc.left+leftOffset;
	rcRect.y= rc.top+(rc.bottom-rc.top+2-font->Height())/2-1;
	rcRect.h=font->Height()+2;
	rcRect.w=rightOffset-leftOffset;

	render->SetDrawColor(0xff9bd6ff);
	render->FillRect(rcRect);
}

void ZTextInput::PaintTextEditing(ZRenderer* render)
{
	RECT rc = m_rcItem;
	rc.left += m_rcTextPadding.left;
	rc.right -= m_rcTextPadding.right;
	rc.top += m_rcTextPadding.top;
	rc.bottom -= m_rcTextPadding.bottom;

	int selBegin=m_editingBegin;
	int selEnd=m_editingEnd;
	if(selBegin>selEnd)
	{
		selBegin=m_editingEnd;
		selEnd=m_editingBegin;
	}

	if (selEnd<m_scroll)
		return;
	if (selBegin<m_scroll)
		selBegin=m_scroll;


	RefCountedPtr<ZFont> font=GetFont();

	int leftOffset=0,rightOffset=0;
	for (int i=m_scroll;i<m_textUtf16.GetLength();++i)
	{
		int w=font->CharWidth(m_textUtf16[i]);
		if (selBegin>i)
		{
			leftOffset+=w;
		}
		if (selEnd>i)
		{
			rightOffset+=w;
		}
	}

	int y=rc.top+(rc.bottom-rc.top+2-font->Height())/2+font->Height();
	render->DrawLine(rc.left+leftOffset,y,rc.left+rightOffset,y);
}






IMPL_ZCONTROL_FACTORY("TextInput",ZTextInput);

}